----------------------------------------------------------------------------------
-- CPEG 422 spartan-3e base
-- Jason Parrott
-- Rodney McGee
-- Ray Delvecchio
-- Juergen Ributzka
-- Jack Welsh
-- Ryan Hoover
-- Mike Stamat
-- Lee Reed
--
-- LCD Code
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity LCD is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           data_in : in  STD_LOGIC_VECTOR (7 downto 0);
			  enable : in STD_LOGIC;
           LCD_D : out  STD_LOGIC_VECTOR (11 downto 8);
           LCD_E : out  STD_LOGIC;
           finished : out  STD_LOGIC);
end LCD;

architecture Behavioral of LCD is

type statetype_init is (I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13);
type statetype_write is (W0, W1, W2, W3, W4, W5);

signal state_init, next_state_init : statetype_init := I0;
signal state_write, next_state_write : statetype_write := W0;

signal InitComplete: std_logic := '0';

begin

state_init <= next_state_init;
state_write <= next_state_write;

LCD_Init: process(clk, reset)
variable count: std_logic_vector (19 downto 0) := x"00000";
variable dispWait: integer := 0;
begin

	if (reset = '1') then
		count := x"00000";
		InitComplete <= '0';
		finished <= '0';
		dispWait := 0;
	elsif (rising_edge(clk)) then
	
		--Default is zero, but may be overwritten in the case statement
		LCD_E <= '0';
			
		if (InitComplete = '0') then
			
			case state_init is
			
				--Init state I0: wait 15ms (750,000 cycles @ 50 MHz)
				when I0 =>
					if (count = 750000-1) then
						count := x"00000";
						next_state_init <= I1;
					else
						count := count + 1;
						next_state_init <= I0;
					end if;
				
				--Init state I1-I2: write 0x3
				--I1: wait 2 cycles before asserting enable (LCD_E)
				--I2: assert LCD_E, keep high for 12 cycles
				when I1 =>
					LCD_D <= x"3";
					if (count = 2-1) then
						count := x"00000";
						next_state_init <= I2;
					else
						count := count + 1;
						next_state_init <= I1;
					end if;
					
				when I2 =>
					LCD_D <= x"3";
					LCD_E <= '1';
					if (count = 12-1) then
						count := x"00000";
						next_state_init <= I3;
					else
						count := count + 1;
						next_state_init <= I2;
					end if;
					
				--Init state I3: wait 4.1ms (205,000 cycles @ 50 MHz)
				when I3 =>
					if (count = 205000-1) then
						count := x"00000";
						next_state_init <= I4;
					else
						count := count + 1;
						next_state_init <= I3;
					end if;
					
				--Init state I4-I5: write 0x3
				--I4: wait 2 cycles before asserting enable (LCD_E)
				--I5: assert LCD_E, keep high for 12 cycles
				when I4 =>
					LCD_D <= x"3";
					if (count = 2-1) then
						count := x"00000";
						next_state_init <= I5;
					else
						count := count + 1;
						next_state_init <= I4;
					end if;
					
				when I5 =>
					LCD_D <= x"3";
					LCD_E <= '1';
					if (count = 12-1) then
						count := x"00000";
						next_state_init <= I6;
					else
						count := count + 1;
						next_state_init <= I5;
					end if;
					
				--Init state I6: wait 100us (5,000 cycles @ 50 MHz)
				when I6 =>
					if (count = 5000-1) then
						count := x"00000";
						next_state_init <= I7;
					else
						count := count + 1;
						next_state_init <= I6;
					end if;
			
				--Init state I7-I8: write 0x3
				--I7: wait 2 cycles before asserting enable (LCD_E)
				--I8: assert LCD_E, keep high for 12 cycles
				when I7 =>
					LCD_D <= x"3";
					if (count = 2-1) then
						count := x"00000";
						next_state_init <= I8;
					else
						count := count + 1;
						next_state_init <= I7;
					end if;
					
				when I8 =>
					LCD_D <= x"3";
					LCD_E <= '1';
					if (count = 12-1) then
						count := x"00000";
						next_state_init <= I9;
					else
						count := count + 1;
						next_state_init <= I8;
					end if;
				
				--Init state I9: wait 40us (2,000 cycles @ 50 MHz)
				when I9 =>
					if (count = 2000-1) then
						count := x"00000";
						next_state_init <= I10;
					else
						count := count + 1;
						next_state_init <= I9;
					end if;
					
				--Init state I10-I11: write 0x2
				--I10: wait 2 cycles before asserting enable (LCD_E)
				--I11: assert LCD_E, keep high for 12 cycles
				when I10 =>
					LCD_D <= x"2";
					if (count = 2-1) then
						count := x"00000";
						next_state_init <= I11;
					else
						count := count + 1;
						next_state_init <= I10;
					end if;
					
				when I11 =>
					LCD_D <= x"2";
					LCD_E <= '1';
					if (count = 12-1) then
						count := x"00000";
						next_state_init <= I12;
					else
						count := count + 1;
						next_state_init <= I11;
					end if;
					
				--Init state I12: wait 40us (2,000 cycles @ 50 MHz)	
				when I12 =>
					if (count = 2000-1) then
						count := x"00000";
						next_state_init <= I13;
					else
						count := count + 1;
						next_state_init <= I12;
					end if;
					
				--Init state I13: set flag to indicate initialization complete
				--Spin at current state
				when I13 =>
					InitComplete <= '1';
					next_state_init <= I13;
					finished <= '1';
					
				when others =>
					null;
					
			end case;
	
		elsif (InitComplete = '1') then
			LCD_E <= '0';
			
			-- If enable is high, write data to LCD
			if (enable = '1') then
				finished <= '0';
				
				case state_write is
				
					--Write state W0-W1: transmit first 4-bits of data byte
					--W0: wait 2 cycles before asserting enable (LCD_E)
					--W1: assert LCD_E, keep high for 12 cycles
					-- If instruction is 'Clear Display', set flag
					-- because this affects timing requirements
					when W0 =>
						LCD_D <= data_in(7 downto 4);
						
						if (data_in = x"01") then
							dispWait := 82000;
						else
							dispWait := 2000;
						end if;
						
						if (count = 2-1) then
							count := x"00000";
							next_state_write <= W1;
						else
							count := count + 1;
							next_state_write <= W0;
						end if;
						
					when W1 =>
						LCD_E <= '1';
						LCD_D <= data_in(7 downto 4);
						if (count = 12-1) then
							count := x"00000";
							next_state_write <= W2;
						else
							count := count + 1;
							next_state_write <= W1;
						end if;
						
					--Write state W2-W3: transmit second 4-bits of data byte
					--W2: wait 2 cycles before asserting enable (LCD_E)
					--W3: assert LCD_E, keep high for 12 cycles
					when W2 =>
						LCD_D <= data_in(3 downto 0);
						if (count = 50-1) then
							count := x"00000";
							next_state_write <= W3;
						else
							count := count + 1;
							next_state_write <= W2;
						end if;
						
					when W3 =>
						LCD_E <= '1';
						LCD_D <= data_in(3 downto 0);
						if (count = 12-1) then
							count := x"00000";
							next_state_write <= W4;
						else
							count := count + 1;
							next_state_write <= W3;
						end if;
						
					--Write state W4: wait 40us (2,000 cycles @ 50 MHz) before
					--writing next byte of data
					-- If instruction was clear display, wait 82,000 cycles
					when W4 =>
						if (count = dispWait-1) then
							count := x"00000";
							next_state_write <= W5;
						else
							count := count + 1;
							next_state_write <= W4;
						end if;
						
					--Write state W5: indicate write is finished
					when W5 =>
						finished <= '1';
						next_state_write <= W5;
						
					when others =>
						null;
					
				end case;
				
			--If enable is low, assert finished, and reset write state
			else
				finished <= '1';
				next_state_write <= W0;
			end if;
		end if;
	end if;
end process;

end Behavioral;

